/***************************************************************************
 *   Copyright (C) 2005 by xihe & Mike  *
 *   xihels@163.com                     *
 *   mikeandmore@gmail.com              *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "FriendList.h"
#include <stdlib.h>
#include "Packet.h"



/*
 * =====================================================================================
 *        Class:  GetFriendListPacket
 *  Description:  请求好友列表
 * =====================================================================================
 */

int GetFriendListPacket::putBody(uint8_t* buf) const {
    uint8_t* cur = buf;
    cur += writeBufW(cur, startPosition);
    cur += writeBufB(cur, QQ_FRIEND_LIST_SORTED);
    cur += writeBufW(cur, 0x0001);

    return cur - buf;
}


/*
 * =====================================================================================
 *        Class:  GetFriendListReplyPacket
 *  Description:  好友列表的回复
 * =====================================================================================
 */
GetFriendListReplyPacket::
GetFriendListReplyPacket(const uint8_t* buf,
	int len) :
    InPacket(buf, len, true)
{
    const uint8_t* cur = buf + QQ_HEADER_LENGTH_BASIC_FAMILY_IN;
    // 下一次好友列表开始位置
    cur += readBufW(position, cur);

    QQBuddy buddy;
    int buddylen;
    const uint8_t* max = buf + len;
    members.reserve((max - cur) / sizeof (QQBuddy) + 1);
    while (cur < max) {
	cur += buddy.getBody(cur, max - cur);
	members.push_back(buddy);
    }	

    if (cur - buf > len)
	throw runtime_error("GetFriendListReplyPacket() Error: BUFFER len not enough");
}


/*
 * =====================================================================================
 *        Class:  GetFriendOnlinePacket
 *  Description:  请求在线好友列表
 * =====================================================================================
 */
int GetFriendOnlinePacket::putBody(uint8_t* buf) const
{
    uint8_t* cur = buf;
    cur += writeBufB(cur, 0x02);
    cur += writeBufB(cur, startPosition);
    cur += writeBufB(cur, 0x00);
    cur += writeBufW(cur, 0x0000);

    return cur - buf;
}


/*
 * =====================================================================================
 *        Class:  GetFriendOnlineReplyPacket
 *  Description:  在线好友的回复包
 * =====================================================================================
 */
GetFriendOnlineReplyPacket::
GetFriendOnlineReplyPacket(const uint8_t* buf,
	int len) :
    InPacket(buf, len, true)
{
    /**
      得到在线好友列表的应答包，格式为
      1. 头部
      2. position，和GetFriendListReplyPacket中的position一样
      3. 31字节的QQBuddyStatus结构
      4. 2个未知自己
      5. 1个字节扩展标志
      6. 1个字节通用标志
      7. 2个未知字节
      8. 结束符，0，应该是c语言的关系，当作字符串结尾
      9. 尾部
      */
    const uint8_t* cur = buf + QQ_HEADER_LENGTH_BASIC_FAMILY_IN;
    uint16_t pos = 0;

    cur += readBufB(position, cur);

    members.reserve( (len - (cur - buf)) / 38 + 1);
    QQBuddyStatus status;

    const uint8_t* max = buf + len;
    members.reserve((max - cur) / sizeof (QQBuddyStatus) + 1);
    while (cur < max) {
	cur += status.getBody(cur, 31);
	members.push_back(status);
	cur += 7;
    }

    if (cur - buf > len)
	throw runtime_error("GetFriendOnlineReplyPacket() Error: BUFFER len not enough");

}

/**
 得到好友等级的请求包
 1. 头部
 2. byte 0: 总是0x00 可能是子命令， 目前只有0x00
 3. byte 1-4: 好友QQ号
 4. 如果是多个好友，重复第3部
 6. 尾部 
*/


GetFriendLevelPacket::GetFriendLevelPacket(QQClient& cl, const Members _members):
    OutPacket(cl, QQ_CMD_GET_LEVEL),
    members(_members)
{}

int GetFriendLevelPacket::putBody(uint8_t* buf) const 
{
    uint8_t* cur = buf;
    *cur++ = 0x00;

    Members::const_iterator first = members.begin();
    Members::const_iterator last  = members.end();
    
    for (; first != last; ++first)
	writeBufDw(cur, *first);
    
    return 1 + members.size() * 4;
}

/*
  好友等级请求应答包，格式为
 1. 头部
 2. byte 0: 总是0x00 可能是子命令， 不确定
 3. byte 1- 4: 好友QQ号
 4. byte 5- 8: 登录总时长， 秒为单位
 5. byte 9-10: 等级
 6. byte 11－12: 升级剩余时长，小时为单位
 7. 如果是多个好友，重复第3到6部分
 8. 尾部 
*/
GetFriendLevelReplyPacket::GetFriendLevelReplyPacket(const uint8_t* buf, int len) :
    InPacket(buf, len)
{
    const uint8_t* cur = buf + QQ_HEADER_LENGTH_BASIC_FAMILY_IN;
    cur += readBufB(replyCode, cur);

    const uint8_t* max = buf + len;

    members.reserve((max - cur) / sizeof (QQBuddyLevel) + 1);
      while (cur < max) {
	members.push_back(QQBuddyLevel(cur, max - cur));
	cur += 12;
    }

    if (cur - buf > len)
	throw runtime_error("GetFriendLeveReplyPacket() Error: BUFFER len not enough");
}


/**
  好友状态改变包，这个是从服务器发来的包，格式为
  1. 头部
  2. FriendStatus结构
  3. 用户的QQ号，注意不是好友的，是用户的，四字节
  4. 尾部
  */


FriendChangeStatusPacket::
FriendChangeStatusPacket(const uint8_t* buf,
	int len) : 
    InPacket(buf, len)
{
    if (len < friendStatus.getBody(buf + QQ_HEADER_LENGTH_BASIC_FAMILY_IN,
	    len - QQ_HEADER_LENGTH_BASIC_FAMILY_IN))
	throw runtime_error("FriendChangeStatusPacket() Error: BUFFER len not enough");
}

/**
 * 得到用户的信息，格式为
 * 1. 头部
 * 2. 用户QQ号的字符串形式，然后加密
 * 3. 尾部
 */

int GetUserInfoPacket::putBody(uint8_t* buf) const
{
    return sprintf(reinterpret_cast<char*>(buf), "%d", uid);
}


/**
 * 请求用户信息的回复包，格式为
 * 1. 头部
 * 2. 由ascii 30分隔的各个字段
 * 3. 尾部
 */

GetUserInfoReplyPacket::
GetUserInfoReplyPacket(const uint8_t* buf,
	int len) :
    InPacket(buf, len, true)
{
    contact = new QQContact(reinterpret_cast<const char*>(
		buf + QQ_HEADER_LENGTH_BASIC_FAMILY_IN),
	    len - QQ_HEADER_LENGTH_BASIC_FAMILY_IN);
    uid = atoi(contact->infos[0]);
}

/**
 * 搜索在线用户的包，格式为
 * 1. 头部
 * 2. 1个字节，表示搜索类型，比如搜索全部在线用户是0x31，自定义搜索是0x30
 * 3. 1字节分隔符: 0x1F
 * 4. 搜索参数
 * 	  i.  对于搜索全部在线用户的请求，是一个页号，用字符串表示，从0开始
 *    ii. 对于自定义搜索类型，是4个域，用0x1F分隔，依次是
 * 		   a. 要搜索的用户的QQ号的字符串形式
 * 		   b. 要搜索的用户的昵称
 * 		   c. 要搜索的用户的email
 *         d. 页号的字符串形式，这后面没有分隔符了，是用0x0结尾的         
 * 5. 尾部
 */
SearchUserPacket::SearchUserPacket(QQClient& cl, int _page) :
    OutPacket(cl, QQ_CMD_SEARCH_USER),
    page(int2string(_page)),
    searchType(QQ_SEARCH_ALL)
{}

SearchUserPacket::SearchUserPacket(QQClient& cl, 
		const string& _qqstr,
	       	const string& _nickname,
		const string& _email,
		int _page,
        bool match):
    OutPacket(cl, QQ_CMD_SEARCH_USER),
    qqstr(_qqstr),
    nickname(_nickname),
    email(_email),
    page(int2string(_page)),
    matchEntireString(match),
    searchType(QQ_SEARCH_CUSTOM)
{
}

int SearchUserPacket::putBody(uint8_t* buf) const
{    
    uint8_t* cur = buf;

	if(searchType == QQ_SEARCH_ALL) 
	{
		cur += writeBufB(cur, searchType);
		cur += writeBufB(cur, DIVIDER);
		cur += writeBufData(cur, (uint8_t*)page.c_str(), page.length());
	}
    else if(searchType == QQ_SEARCH_CUSTOM)
    {
		cur += writeBufB(cur, searchType);
        cur += writeBufB(cur, DIVIDER);
						
		if(!qqstr.length()) 
			cur += writeBufB(cur, NULL_FIELD);
		else
			cur += writeBufData(cur, (uint8_t*)qqstr.c_str(), qqstr.length());
		
		cur += writeBufB(cur, DIVIDER);	
		
		if(!nickname.length()) 
			cur += writeBufB(cur,NULL_FIELD);
		else
        {
			cur += writeBufData(cur, (uint8_t*)nickname.c_str(),  nickname.length());
			if(!matchEntireString)
				cur += writeBufB(cur, PERCENT);
		}
		cur += writeBufB(cur, DIVIDER);
		
		if(!email.length())
			cur += writeBufB(cur, NULL_FIELD);
		else {
			cur +=writeBufData(cur, (uint8_t*)email.c_str(), email.length());
			if(!matchEntireString)
				cur += writeBufB(cur, PERCENT);
		}
		cur += writeBufB(cur,DIVIDER);
	
		cur +=writeBufData(cur,  (uint8_t*)page.c_str(), page.length());
		
		cur += writeBufB(cur, 0x00);
	}

    return cur - buf;
	
}

/**
 * 搜索在线用户的回复包，格式为
 * 1. 头部
 * 2. 有两种形式
 *    第一种为搜索到了用户
 * 	  以0x1F相隔的用户数据，其中，一个用户的数据分4个域，域之间用0x1E相隔，四个域为
 * 	  i.   用户QQ号的字符串形式
 *    ii.  用户昵称
 *    iii. 用户所在地区
 *    iv.  用户的头像号码
 *    第二种是没有更多的匹配了，表示本次搜索的全部匹配已取得
 *    i. 字符串"-1"
 * 3. 尾部
 */

SearchUserReplyPacket::SearchUserReplyPacket(const uint8_t* buf, int len) :
    InPacket(buf, len)
{
    const uint8_t* cur = buf + QQ_HEADER_LENGTH_BASIC_FAMILY_IN;

    if (len < 2 || '-' == *cur && '1' == *(cur + 1))
	return;
    QQOnlineUserInfo info;
    const uint8_t* max = buf + len;
    for (; cur < max; ) {
	cur += info.getBody(reinterpret_cast<const char*>(cur), max - cur);
	members.push_back(info);
	cur++;
    }

    if (cur - buf > len)
	throw  runtime_error("SearchUserReplyPacket() Error: BUFFER len not enough");

}

/**
 这个是添加好友的时候用的包，这个和AddFriendAuth有什么关系呢，AddFriend
 是最初始的请求包，要加一个好友的时候，首先发这个包，如果对方不需要验证，那
 就是成功了，如果需要验证，就还要继续发请求。这个包格式是
 1. 头部
 2. 要加的人的QQ号的字符串形式
 3. 尾部
 */
int AddFriendPacket::putBody(uint8_t* buf) const
{
    return sprintf(reinterpret_cast<char*>(buf), "%d", uid);
}

/**
 这个是添加好友的应答包，QQ2005 final 格式是
1. 头部
2. 要添加的好友的QQ号，4字节
3. 回复码，1字节

如果回复码是0x00，表示可以加对方为好友
4. 对方验证状态，1字节，0x00 表示不需要验证，0x01 表示需要验证，0x02表示对方拒绝别人加其为好友
5. 尾部

如果回复码是0x99，表示对方已经是好友了
4. 尾部 
 */

AddFriendReplyPacket::AddFriendReplyPacket(const uint8_t* buf, int len) :
    InPacket(buf, len)
{
    const uint8_t* cur = buf + QQ_HEADER_LENGTH_BASIC_FAMILY_IN;
	
	uint32_t uid;
	cur += readBufDw(uid,cur);
	uint8_t rep=*cur;
	if(rep==0x00)
		replyCode = *(++cur);
    else
		replyCode=*cur;
	
    if (cur - buf > len)
	throw (runtime_error("AddFriendReplyPacket() Error: BUFFER len not enough"));
}

AddFriendAuthPacket::AddFriendAuthPacket(QQClient& cl,
	uint32_t _uid,bool _addback,uint8_t _replyCode,
	uint8_t _mlen,string _message)
	:OutPacket(cl,QQ_CMD_ADD_FRIEND_AUTH),
	uid(_uid),
	addback(_addback),
	replyCode(_replyCode),
	mlen(_mlen),
	message(_message)
{
}

/*
添加好友验证协议
 1. 头部
 2. 对方验证状态，1字节
 3. 要添加好友的QQ号，4字节
 4. 是否允许对方加自己为好友，1字节，0x01允许，0x02不允许
 5. 把好友加到第几组，1字节，我的好友组是0x00，然后以此类推

如果对方验证状态为0x00，表示对方不需要验证
 6. 尾部

如果对方验证状态为0x02，表示对方需要验证
 6. 验证消息字节长度，1字节
 7. 验证消息，6得到的长度
 8. 尾部 
*/
int AddFriendAuthPacket::putBody(uint8_t* buf)
{
	uint8_t* cur = buf;
	cur += writeBufB(cur,replyCode);
	cur += writeBufDw(cur,uid);
	if(addback)
		cur += writeBufB(cur,0x01);
	else
		cur += writeBufB(cur,0x02);
	cur += writeBufB(cur,0x00);
	if(replyCode==0x00)
		return cur - buf;
	else
	{
		cur += writeBufB(cur,mlen);
		cur += writeBufData(cur,(uint8_t*)message.c_str(),message.size());
	}
	return  cur - buf;
}

/*添加好友验证应答包

注释: (QQ2005)
====================================

应答包
1. 头部
2. 对方验证状态，1字节 0x00表示对方不需要验证，0x02表示对方需要验证
3. 要添加的好友的QQ号，4字节
4. 回复码，1字节，0x00 表示成功
5. 尾部 
*/

AddFriendAuthReplyPacket::AddFriendAuthReplyPacket(const uint8_t* buf,int len)
	:InPacket(buf,len)
{
	const uint8_t* cur = buf + QQ_HEADER_LENGTH_BASIC_FAMILY_IN;
	
	uint8_t statue;
	cur += readBufB(statue,cur);
	cur += readBufDw(uid,cur);
	cur += readBufB(rplcode,cur);
	
	if(cur-buf>len)
		throw (runtime_error("AddFriendAuthReplyPacket(): BUFFER len not enough"));
}
